{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph\n",
    "from langgraph.graph.message import add_messages\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.callbacks import StdOutCallbackHandler\n",
    "from langchain_core.runnables import RunnableConfig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "class State(TypedDict):\n",
    "# Messages have the type \"list\". The `add_messages` function\n",
    "# in the annotation defines how this state key should be updated\n",
    "# (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36;1m\u001b[1;3m[0:tasks]\u001b[0m \u001b[1mStarting step 0 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3m__start__\u001b[0m -> {'messages': ('user', '你是谁？')}\n",
      "\u001b[36;1m\u001b[1;3m[0:writes]\u001b[0m \u001b[1mFinished step 0 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3mmessages\u001b[0m -> ('user', '你是谁？')\n",
      "\u001b[36;1m\u001b[1;3m[1:tasks]\u001b[0m \u001b[1mStarting step 1 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3mchatbot\u001b[0m -> {'messages': [HumanMessage(content='你是谁？', id='b7eacf36-a577-4b78-a987-740653e11656')]}\n",
      "State <class '__main__.State'>\n",
      "Assistant: 我是一个虚拟助手，可以回答你的问题和提供帮助。有什么我可以帮助你的吗？\n",
      "\u001b[36;1m\u001b[1;3m[1:writes]\u001b[0m \u001b[1mFinished step 1 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3mmessages\u001b[0m -> [AIMessage(content='我是一个虚拟助手，可以回答你的问题和提供帮助。有什么我可以帮助你的吗？', response_metadata={'token_usage': {'completion_tokens': 41, 'prompt_tokens': 12, 'total_tokens': 53}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-1e5fe044-d5aa-40d0-980e-cdd0ac961084-0')]\n",
      "\u001b[36;1m\u001b[1;3m[0:tasks]\u001b[0m \u001b[1mStarting step 0 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3m__start__\u001b[0m -> {'messages': ('user', '你是我么？')}\n",
      "\u001b[36;1m\u001b[1;3m[0:writes]\u001b[0m \u001b[1mFinished step 0 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3mmessages\u001b[0m -> ('user', '你是我么？')\n",
      "\u001b[36;1m\u001b[1;3m[1:tasks]\u001b[0m \u001b[1mStarting step 1 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3mchatbot\u001b[0m -> {'messages': [HumanMessage(content='你是我么？', id='f7b329c8-fcb0-4647-91be-1075baf37095')]}\n",
      "State <class '__main__.State'>\n",
      "Assistant: 不，我是一个人工智能助手，不是您。我是由人类编程和训练而成的。如果您有任何问题或需要帮助，请随时告诉我。\n",
      "\u001b[36;1m\u001b[1;3m[1:writes]\u001b[0m \u001b[1mFinished step 1 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3mmessages\u001b[0m -> [AIMessage(content='不，我是一个人工智能助手，不是您。我是由人类编程和训练而成的。如果您有任何问题或需要帮助，请随时告诉我。', response_metadata={'token_usage': {'completion_tokens': 55, 'prompt_tokens': 12, 'total_tokens': 67}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-3de2aeee-a81c-4d20-a4e3-e4881c977bc7-0')]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "handler = StdOutCallbackHandler()\n",
    "\n",
    "llm = ChatOpenAI(api_key=\"sk-uRUknVzF2daRmidJ227fEa256c5d4f4b9f9a7bB6CeCd0176\", \n",
    "                 base_url=\"https://api.openai-hub.com/v1\", \n",
    "                 callbacks=[handler])\n",
    "\n",
    "def chatbot(state: State):\n",
    "    print(\"State\", State)\n",
    "    input = state[\"messages\"]\n",
    "    result = llm.invoke(input)\n",
    "    return {\"messages\": [result]}\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "graph_builder.set_entry_point(\"chatbot\")\n",
    "\n",
    "graph_builder.set_finish_point(\"chatbot\")\n",
    "\n",
    "graph = graph_builder.compile()\n",
    "\n",
    "runnableConfig = RunnableConfig(callbacks=[handler])\n",
    "\n",
    "while True:\n",
    "    user_input = input(\"User: \")\n",
    "    if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "        print(\"Goodbye!\")\n",
    "        break\n",
    "    for event in graph.stream({\"messages\": (\"user\", user_input)}, debug=True):\n",
    "        for value in event.values():\n",
    "            print(\"Assistant:\", value[\"messages\"][-1].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "123\n",
      "234\n",
      "quit\n",
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "while True:\n",
    "    user_input = input(\"User: \")\n",
    "    print(user_input)\n",
    "    if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "        print(\"Goodbye!\")\n",
    "        break\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36;1m\u001b[1;3m[0:tasks]\u001b[0m \u001b[1mStarting step 0 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3m__start__\u001b[0m -> HumanMessage(content='What is 1 + 1?')\n",
      "\u001b[36;1m\u001b[1;3m[0:writes]\u001b[0m \u001b[1mFinished step 0 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3m__root__\u001b[0m -> HumanMessage(content='What is 1 + 1?')\n",
      "\u001b[36;1m\u001b[1;3m[1:tasks]\u001b[0m \u001b[1mStarting step 1 with 1 task:\n",
      "\u001b[0m- \u001b[32;1m\u001b[1;3moracle\u001b[0m -> [HumanMessage(content='What is 1 + 1?', id='357eee51-6fe1-4fa0-bee1-8931e2ab0acb')]\n",
      "call_oracle [HumanMessage(content='What is 1 + 1?', id='357eee51-6fe1-4fa0-bee1-8931e2ab0acb')]\n",
      "\u001b[36;1m\u001b[1;3m[1:writes]\u001b[0m \u001b[1mFinished step 1 with writes to 1 channel:\n",
      "\u001b[0m- \u001b[33;1m\u001b[1;3m__root__\u001b[0m -> AIMessage(content='1 + 1 equals 2.', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 15, 'total_tokens': 23}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-00e9d0cf-e4b8-4e18-a17f-5cb0cc3df217-0')\n",
      "[HumanMessage(content='What is 1 + 1?', id='357eee51-6fe1-4fa0-bee1-8931e2ab0acb'), AIMessage(content='1 + 1 equals 2.', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 15, 'total_tokens': 23}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-00e9d0cf-e4b8-4e18-a17f-5cb0cc3df217-0')]\n",
      "[HumanMessage(content='What is 1 + 1?', id='357eee51-6fe1-4fa0-bee1-8931e2ab0acb'), AIMessage(content='1 + 1 equals 2.', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 15, 'total_tokens': 23}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-00e9d0cf-e4b8-4e18-a17f-5cb0cc3df217-0')]\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "from langchain_core.runnables import RunnableConfig\n",
    "from langgraph.graph import END, MessageGraph\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "handler = StdOutCallbackHandler()\n",
    "\n",
    "llm = ChatOpenAI(api_key=\"sk-uRUknVzF2daRmidJ227fEa256c5d4f4b9f9a7bB6CeCd0176\", \n",
    "                 base_url=\"https://api.openai-hub.com/v1\", \n",
    "                 callbacks=[handler])\n",
    "\n",
    "graph = MessageGraph()\n",
    "\n",
    "def call_oracle(messages: list):\n",
    "    print(\"call_oracle\", messages)\n",
    "    return llm.invoke(messages)\n",
    "\n",
    "prompt = ChatPromptTemplate.format_messages([\n",
    "    (\"system\", \"You are a helpful assistant named {name} who always speaks in pirate dialect\"),\n",
    "    MessagesPlaceholder(variable_name=\"messages\"),\n",
    "])\n",
    "\n",
    "chain = prompt | llm\n",
    "\n",
    "graph.add_node(\"oracle\", call_oracle)\n",
    "graph.add_edge(\"oracle\", END)\n",
    "\n",
    "graph.set_entry_point(\"oracle\")\n",
    "\n",
    "runnable = graph.compile()\n",
    "\n",
    "runnableConfig = RunnableConfig(callbacks = [handler])\n",
    "\n",
    "response = runnable.invoke(HumanMessage(\"What is 1 + 1?\"), debug=True)\n",
    "for item in response:\n",
    "    print(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.37"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add(x: int, y: int) -> int:\n",
    "    return x+y\n",
    "\n",
    "add(3.14, 1.23)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'x': int, 'y': int, 'return': int}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add.__annotations__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': <class 'int'>, 'b': <class 'str'>, 'mess': typing.Annotated[list, <function add_message at 0x112f49940>]}\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Derived' object has no attribute 'mess'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[13], line 16\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[38;5;28mprint\u001b[39m(Derived\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__annotations__\u001b[39m)\n\u001b[1;32m     14\u001b[0m de \u001b[38;5;241m=\u001b[39m Derived()\n\u001b[0;32m---> 16\u001b[0m \u001b[38;5;28mprint\u001b[39m(de\u001b[38;5;241m.\u001b[39ma, de\u001b[38;5;241m.\u001b[39mb, \u001b[43mde\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmess\u001b[49m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m1\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m2\u001b[39m\u001b[38;5;124m'\u001b[39m))\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Derived' object has no attribute 'mess'"
     ]
    }
   ],
   "source": [
    "def add_message(left: str, right: str) -> str:\n",
    "    return left + \"_\" + right \n",
    "\n",
    "class Base:\n",
    "    a: int = 3\n",
    "    b: str = 'abc'\n",
    "    mess: Annotated[list, add_message]\n",
    "\n",
    "class Derived(Base):\n",
    "    pass\n",
    "\n",
    "print(Derived.__annotations__)\n",
    "\n",
    "de = Derived()\n",
    "\n",
    "print(de.a, de.b, de.mess('1', '2'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1_2\n",
      "[12, 1]\n"
     ]
    }
   ],
   "source": [
    "def add_message(left: str, right: str) -> str:\n",
    "    return left + \"_\" + right\n",
    "\n",
    "T1 = Annotated[list, add_message]\n",
    "print(T1.__metadata__[0](\"1\", \"2\"))\n",
    "\n",
    "asd: T1 = [12]\n",
    "asd.append(1)\n",
    "print(asd)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1_2\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "T1 = Annotated[int, add_message]\n",
    "print(T1.__metadata__[0](\"1\", \"2\"))\n",
    "\n",
    "asd = T1(1)\n",
    "print(asd + 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': <class 'str'>, 'age': <class 'int'>, 'height': <class 'float'>}\n",
      "<class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "from typing import TypedDict\n",
    "\n",
    "\n",
    "class Student(TypedDict):\n",
    "    name: str\n",
    "    age: int\n",
    "    height: float\n",
    "\n",
    "\n",
    "print(Student.__annotations__)\n",
    "\n",
    "s1: Student = {\n",
    "    \"name\": \"xiao ming\",\n",
    "    \"age\": 22,\n",
    "    \"height\": 55.5\n",
    "}\n",
    "\n",
    "print(type(s1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n"
     ]
    }
   ],
   "source": [
    "from typing import Optional, Callable\n",
    "\n",
    "def handler(x: int) -> Optional[int]:\n",
    "    if x % 2 == 0:\n",
    "        return x\n",
    "\n",
    "def handler2(func: Callable[[int], Optional[int]]):\n",
    "    print(func(12))\n",
    "\n",
    "handler2(handler)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
